const drawVS = `
in float position;
uniform sampler2D u_particlesTexture;
out vec2 v_particle_pos;

void main() {
    float particles_res = Weget_particleStateResolution();
    vec2 st = vec2(
        fract(position / particles_res),
        floor(position / particles_res) / particles_res);

    vec4 color = texture(u_particlesTexture, st);

    // decode current particle position from the pixel's RGBA value
    v_particle_pos = vec2(
        color.r / 255.0 + color.b,
        color.g / 255.0 + color.a);

    gl_PointSize = Weget_pointSize();
    gl_Position = vec4(2.0 * v_particle_pos.x - 1.0, 1.0 - 2.0 * v_particle_pos.y, 0, 1);
}
`;

const drawFS = `
precision highp float;
in vec2 v_particle_pos;
void main() {
    vec4 color = vec4(1.0);

    bool colorize = Weget_colorize() > 0.0;
    if(colorize){
        float scale = Weget_scale();
        float offset = Weget_offset();
        float maxValue = Weget_maxValue();
        bool shouldAnime = Weget_shouldAnime() > 0.0;
        bool shouldFlipY = Weget_shouldFlipY() > 0.0;
        vec2 st = v_particle_pos;
        st.y = 1.0 - st.y;
       
        vec4 worldExtent = Weget_extent();
        vec4 localExtent = Weget_localExtent();
        vec2 lonLat = WeGetImageryLonLat(st,localExtent);
        vec2 uv = WeGetImageryLocalUV(lonLat,worldExtent);

        uv.x = mod(uv.x,1.0);
        if(shouldFlipY){
            uv.y = 1.0 - uv.y;
        }
        vec2 velocity = WeDecodeVal(u_BaseTexture,uv).rg;
        if(shouldAnime){
            float animeStep = Weget_animeStep();
            vec2 lastComp = WeDecodeVal(u_LastBaseTexture,uv).rg;
            velocity = mix(lastComp,velocity,animeStep);
        }

        float speed = length(velocity);
        float i = speed / maxValue;
        color = texture(u_TFTexture,vec2(i,0.0));
        if(speed < 2.0) color.a = 0.5;
    }
    out_FragColor = color;
}
`;

const quadVS = `
in vec3 position;
in vec2 st;

out vec2 textureCoordinate;

void main() 
{
    textureCoordinate = st;
    gl_Position = vec4(position, 1.0);
}
`;

const updateFS = `
precision highp float;
uniform sampler2D u_particlesTexture;
uniform float u_rand_seed;
in vec2 textureCoordinate;

// pseudo-random generator
const vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453);
float rand(const vec2 co) {
    float t = dot(rand_constants.xy, co);
    return fract(sin(t) * (rand_constants.z + t));
}

// wind speed lookup; use manual bilinear filtering based on 4 adjacent pixels for smooth interpolation
vec2 lookup_wind(sampler2D windTex,const vec2 v_particle_pos) {
    bool shouldFlipX = Weget_shouldFlipX() > 0.0;
    bool shouldFlipY = Weget_shouldFlipY() > 0.0;

    float windResWidth = Weget_windResWidth();
    float windResHeight = Weget_windResHeight();
    vec2 u_windTex_res = vec2(windResWidth,windResHeight);
    vec2 px = 1.0 / u_windTex_res;

    vec2 st = v_particle_pos;
    st.y = 1.0 - st.y;
    
    vec4 worldExtent = Weget_extent();
    vec4 localExtent = Weget_localExtent();
    vec2 lonLat = WeGetImageryLonLat(st,localExtent);
    vec2 uv = WeGetImageryLocalUV(lonLat,worldExtent);
    
    uv.x = mod(uv.x,1.0);
    if(shouldFlipY){
        uv.y = 1.0 - uv.y;
    }
    
    vec2 vc = (floor(uv * u_windTex_res)) * px;
    vec2 f = fract(uv * u_windTex_res);

    vec2 tl = WeDecodeVal(windTex,vc).rg;
    vec2 tr = WeDecodeVal(windTex,vc + vec2(px.x, 0)).rg;
    vec2 bl = WeDecodeVal(windTex,vc + vec2(0, px.y)).rg;
    vec2 br = WeDecodeVal(windTex,vc + px).rg;

    return mix(mix(tl, tr, f.x), mix(bl, br, f.x), f.y);
}

void main() {
    vec2 v_tex_pos = textureCoordinate;
    float u_speed_factor = Weget_speedFactor();
    float u_drop_rate_bump = Weget_dropRateBump();
    float u_drop_rate = Weget_dropRate();
    bool shouldAnime = Weget_shouldAnime() > 0.0;
    
    vec4 color = texture(u_particlesTexture, v_tex_pos);
    vec2 pos = vec2(
        color.r / 255.0 + color.b,
        color.g / 255.0 + color.a); // decode particle position from pixel RGBA

    float maxValue = Weget_maxValue();
    
    vec2 velocity = lookup_wind(u_BaseTexture,pos);

    if(shouldAnime){
        float animeStep = Weget_animeStep();
        vec2 lastComp = lookup_wind(u_LastBaseTexture,pos);
        velocity = mix(lastComp,velocity,animeStep);
    }

    float speed_t = length(velocity) / length(vec2(maxValue,maxValue));

    float d1 = 180.0;
    float d2 = 90.0;

    // take EPSG:4236 distortion into account for calculating where the particle moved
    float distortion = cos(radians(pos.y * d1 - d2));
    vec2 offset = vec2(velocity.x / distortion, -velocity.y) * 0.0001 * u_speed_factor;

    // update particle position, wrapping around the date line
    pos = fract(1.0 + pos + offset);

    // a random seed to use for the particle drop
    vec2 seed = (pos + v_tex_pos) * u_rand_seed;

    // drop rate is a chance a particle will restart at random position, to avoid degeneration
    float drop_rate = u_drop_rate + speed_t * u_drop_rate_bump;
    float drop = step(1.0 - drop_rate, rand(seed));

    if(length(velocity) > 2.0)
    {
      seed = v_tex_pos;
    }

    vec2 random_pos = vec2(
        rand(seed + 1.3),
        rand(seed + 2.1));
    pos = mix(pos, random_pos, drop);

    // encode the new particle position back into RGBA
    out_FragColor = vec4(
        fract(pos * 255.0),
        floor(pos * 255.0) / 255.0);
    }
`;

const screenFS = `
uniform sampler2D screenTexture;
in vec2 textureCoordinate;
void main() {
    float fadeOpacity = Weget_fadeOpacity();
    vec2 uv = textureCoordinate;
    vec4 color = texture(screenTexture,uv);
    out_FragColor = vec4(color.rgb,floor(255.0 * color.a * fadeOpacity) / 255.0);
}
`;

export default {
    screenFS, drawVS, quadVS, drawFS, updateFS,
};
